package org.apache.catalina.startup;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.descriptor.InputSourceUtil;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.descriptor.web.WebXmlParser;
import org.xml.sax.InputSource;

public final class WebXmlUtil {
	private static final Log log = LogFactory.getLog(WebXmlUtil.class);
	private WebXmlUtil() {}
	
	protected static WebXml getWebXml(final javax.servlet.ServletContext servletContext) {
		if(servletContext==null) {
			log.warn("servletContext is null");
			return null;
		}
		InputSource webXmlSource = getContextWebXmlSource(servletContext,Constants.ApplicationWebXml);
		if(webXmlSource==null) {
			return null;
		}
		WebXmlParser webXmlParser = new WebXmlParser(false, true,
                true);
		
		WebXml webXml = createWebXml();
		boolean ok = webXmlParser.parseWebXml(webXmlSource, webXml, false);
		log.info("load and read web.xml,the result is "+ok);
		InputSourceUtil.close(webXmlSource);
		// Ordering is important here

        // Step 1. Identify all the JARs packaged with the application and those
        // provided by the container. If any of the application JARs have a
        // web-fragment.xml it will be parsed at this point. web-fragment.xml
        // files are ignored for container provided JARs.
		InputSource fragmentSource = getContextWebXmlSource(servletContext,Constants.WebFragmentXml);//TODO 目前只支持读取一个web-fragment.xml
		if(fragmentSource==null) {
			return webXml;
		}
		WebXml fragment = createWebXml();
		ok = webXmlParser.parseWebXml(fragmentSource, fragment, false);
		log.info("load and read web-fragment.xml,the result is "+ok);
		InputSourceUtil.close(fragmentSource);
		
		Map<String,WebXml> fragments = new HashMap<>();
		fragments.put(fragment.getName(), fragment);
        // Step 2. Order the fragments.
		Set<WebXml> orderedFragments = null;
        orderedFragments = WebXml.orderWebFragments(webXml, fragments, servletContext);
        // Step 6. Merge web-fragment.xml files into the main web.xml
        // file.
        ok = webXml.merge(orderedFragments);
        log.info("web.xml merge web-fragment.xml,the result is "+ok);
        return webXml;
	}
	
	private static WebXml createWebXml() {
        return new WebXml();
    }
	
	private static InputSource getContextWebXmlSource(final javax.servlet.ServletContext servletContext,final String webxml) {
		InputSource source = null;
		try (InputStream stream = servletContext.getResourceAsStream(webxml);
				ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {
			if (stream == null) {
				if (log.isDebugEnabled()) {
					log.debug("Missing application web.xml, using defaults only");
				}
				return source;
			}
			URL url = servletContext.getResource(webxml);
			if (url == null) {
				if (log.isDebugEnabled()) {
					log.debug("Unable to determine URL for application web.xml");
				}
				return source;
			}
			byte[] buffer = new byte[8192];
		    int bytesRead;
		    while ((bytesRead = stream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
			source = new InputSource(url.toExternalForm());
			// Convert the byte array into a ByteArrayInputStream
	        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(outputStream.toByteArray());
			source.setByteStream(byteArrayInputStream);
		} catch (Exception e) {
			log.error(e);
		}
		return source;
	}
}
